import time
import json
import socket

from codes import MessageType



class Client():
    """
        Implements data exchange with the server
    """
    def __init__(self, addr: tuple, recon_time: int, packet_size: int):
        self._server_addr = addr
        self._reconnect_time = recon_time
        self._packet_size = packet_size
        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)


    @staticmethod
    def _response_generator(status: int, response: dict) -> str:
        """
        :params status:             Operation status
        :params response:           Operation result
        
        :returns                    String to send
        """
        return json.dumps(
            {
                "header": {
                    "type": MessageType.RESPONSE,
                    "status": status
                },
                "body": {
                    "response": response
                }
            })


    @staticmethod
    def _split_message(message: str) -> (str, str):
        """
        :input message:             Server response

        :returns tuple:             Tuple from header and body
        """
        dict_message = json.loads(message)
        return dict_message["header"], dict_message["body"]


    def _try_connect(self):
        """
        This method attempts to establish a connection to the server.
        """
        # TODO delete debug print
        print("[i] Trying to connect...")
        succes = False
        while not succes:
            try:
                self._socket.connect(self._server_addr)
                succes = True
            except Exception as e:
                # TODO: delete debug print
                print(f"[E] {e}")
                succes = False
                time.sleep(self._reconnect_time)


    def _listen(self, callback):
        """
        This method listens on the port to which the server is connected.
        """
        while True:
            data = self._socket.recv(self._packet_size)
            if not data:
                break
            
            decode_data = data.decode("utf-8")
            header, body = self._split_message(decode_data)
            callback(header, body)

        self._socket.close()


    def start(self, callback):
        self._try_connect()
        self._listen(callback)


    def send_response(self, status: int, response=None):
        """
        This method sends data to the server.
        """
        json_data = self._response_generator(status, response)
        data = json_data.encode("utf-8")
        self._socket.send(data)
        